/*
 *  Copyright 2008 The MITRE Corporation (http://www.mitre.org/). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.mitre.lattice.query;

import org.mitre.mrald.util.MetaData;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.mitre.lattice.lattice.LatticeNode;
import org.mitre.mrald.control.AbstractStep;
import org.mitre.mrald.control.MsgObject;
import org.mitre.mrald.control.WorkflowStepException;
import org.mitre.mrald.util.Config;
import org.mitre.mrald.util.MiscUtils;
import org.mitre.mrald.util.MraldConnection;
import org.mitre.mrald.util.MraldException;
import org.mitre.mrald.util.User;
/**
 *@author     Gail Hamilton
 *@created    November 4, 2003
 */

public class LatticeQuery extends AbstractStep
{

	/**
	 *  Constructor for the AllTablesListTag object
	 */
	public LatticeQuery() { }


	/**
	 *  Description of the Method
	 *
	 *@param  msg                        Description of the Parameter
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	public final void execute(MsgObject msg)
		 throws WorkflowStepException
	{
        if ( !Config.useLattice() )
        {
            System.out.println( "not using lattice" );
            return;
        }
		try
		{
			User user = getUser(msg);

			String[] queryOrig = msg.getQuery();
			 for (int i=0; i < queryOrig.length; i++)
			 {
			    String query = adaptQuery(queryOrig[i], user);
			    msg.setQuery(query, i);
			 }
			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "LatticeQuery. EXecute: all msgObject " + msg.toString());
			//MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "LatticeQuery. EXecute: MODIFIED query is " + query);

		} catch (MraldException e)
		{
			throw new WorkflowStepException(e, msg);
		}

	}


	/**
	 *  Description of the Method
	 *
	 *@param  queryOrig  Description of the Parameter
	 *@return            Description of the Return Value
	 */
	public static String adaptQuery(String queryOrig, User user) throws MraldException
	{
		try
		{
//		    GroupList groupList = new GroupList();

		    //MraldOutFile.logToFile(Config.getProperty("LOGFILE"), "LatticeQuery: User is " + user);

		    String keyGroup = user.getGroup();

		    return adaptQuery(queryOrig, keyGroup);
		}
		catch(MraldException e)
		{
			throw new RuntimeException(e);
		}

	}

         /**
	 *  Description of the Method
	 *
	 *@param  queryOrig  Description of the Parameter
	 *@return            Description of the Return Value
	 */
	public static String adaptQuery(String queryOrig, String keyGroup) throws MraldException
	{

		GroupList groupList = new GroupList();
		ArrayList groups = (ArrayList) groupList.getChildGroups(keyGroup);

		 //MraldOutFile.logToFile( Config.getProperty("LOGFILE") , "Lattice QUery : adapted Query. Original :  " + queryOrig );

		//String queryOrigLow = queryOrig.toLowerCase();
		String WhereOrAnd = " AND ";

		String query="";
		String queryOrigLow = queryOrig.toLowerCase();
		//If no 'AND' exists then, need to append Where
		if (queryOrigLow.indexOf("where") < 1)
		{
			WhereOrAnd = " WHERE ";
		}

		String groupListStr = getGroupList(groups);

		ArrayList tableNames = parseTables(queryOrigLow);

		HashMap latticeSql = getLatticeSql(groupListStr);

		StringBuffer temp = buildSql(tableNames, latticeSql);

		String tableList  = buildTableList( tableNames, latticeSql);

		int fromPos=-1;
//		String queryFirst = queryOrig;

		//Put the table into the From List
		if (temp.length() > 0)
		{
			if (queryOrigLow.indexOf("from") > 0 )
			{
			    fromPos = queryOrig.toLowerCase().indexOf("from" ) + 4;
			    query = queryOrig.substring(0, fromPos) + " " + tableList + queryOrig.substring(fromPos );
			    //MraldOutFile.logToFile( Config.getProperty("LOGFILE") , "Lattice QUery : adapted Query. Including new tables :  " + query );

			}

			String queryLow = query.toLowerCase();

			int orderPos = query.length();
			int groupPos = query.length();
			int insertPos = 0;


			if (queryLow.indexOf("order by") > 0 )
			     orderPos = queryLow.indexOf("order by");


			if (queryLow.indexOf("group by") > 0)
			     groupPos = queryLow.indexOf("group by");

			insertPos =Math.min(orderPos, groupPos);
			String ending = query.substring( insertPos);
			query = query.substring(0, insertPos)+ WhereOrAnd + new String(temp) + ending;
			////query = queryOrig + WhereOrAnd + new String(temp);
			//MraldOutFile.logToFile( Config.getProperty("LOGFILE") , "Lattice QUery : adapted QUery " + query );

		} else
		{
			query = queryOrig;
		}
		return query;
	}


	/**
	 *  Description of the Method
	 *
	 *@param  sqlQuery                   Description of the Parameter
	 *@return                            Description of the Return Value
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	public static ArrayList parseTables(String sqlQuery)
		 throws WorkflowStepException
	{
		//Take the SQL and get the tables by parsing the
		//string between FROM and WHERE

		ArrayList<String> tableNames = new ArrayList<String>();

		int whereIndex = sqlQuery.indexOf("where");

		int orderIndex =0;
		int groupIndex =0;

		if (whereIndex < 0)
		{
			whereIndex = sqlQuery.length();

		        orderIndex = sqlQuery.indexOf("order by");

		        if (orderIndex < 0)
		        {
			        orderIndex = sqlQuery.length();
		        }

		        groupIndex = sqlQuery.indexOf("group by");

		         if (groupIndex < 0)
		        {
			        groupIndex = sqlQuery.length();
		        }

			whereIndex = Math.min(orderIndex, groupIndex);
		}
		String tables = sqlQuery.substring((sqlQuery.indexOf("from") + 4), whereIndex);
		StringTokenizer valueTokens = new StringTokenizer(tables, ",");

		/*
		 *  Parse out the "," and loop
		 */
		while (valueTokens.hasMoreTokens())
		{
			String nvp = valueTokens.nextToken();
			nvp = nvp.trim();
			tableNames.add(nvp);

		}
		return tableNames;
	}


	/**
	 *  Gets the latticeSql attribute of the LatticeQuery object
	 *
	 *@param  groupList                  Description of the Parameter
	 *@return                            The latticeSql value
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	public static HashMap getLatticeSql(String groupList)
		 throws WorkflowStepException
	{
        if ( !Config.useLattice() )
        {
            return new HashMap();
        }

		try
		{
			HashMap<String,String> sqlTableList = new HashMap<String,String>();
			//Connect to database
			MraldConnection conn = new MraldConnection( MetaData.ADMIN_DB );
			String query = "select table_name, sql_clause from label_sql ";
			ResultSet r = conn.executeQuery(query);

			while (r.next())
			{
				String tableName = r.getString("table_name").toLowerCase();
				String sql = r.getString("sql_clause");
				sqlTableList.put(tableName, sql + groupList);
			}

			r.close();
			conn.close();
			return sqlTableList;
		} catch (SQLException e)
		{
			throw new WorkflowStepException(e);
		}
	}


	/**
	 *  Description of the Method
	 *
	 *@param  tables                     Description of the Parameter
	 *@param  addSql                     Description of the Parameter
	 *@return                            Description of the Return Value
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	public static StringBuffer buildSql(ArrayList tables, HashMap addSql)
		 throws WorkflowStepException
	{
		StringBuffer newSql = new StringBuffer();

		Iterator iter = addSql.keySet().iterator();

		boolean isFirst = true;
		while (iter.hasNext())
		{

			String tableName = (String) iter.next();

			if (tables.contains(tableName))
			{
				if (!isFirst)
				{
					newSql.append(" AND ");

				} else
				{
					isFirst = false;
				}

				newSql.append((String) addSql.get(tableName));

			}
		}
		return newSql;
	}


		/**
	 * Cyce through the table names and add them to a string list
	 *
	 *@param  groups                     Description of the Parameter
	 *@return                            Description of the Return Value
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	private static String buildTableList(ArrayList tables, HashMap latticeSql)
		 throws WorkflowStepException
	{

		StringBuffer strBuf = new StringBuffer();

		Iterator tableIter = latticeSql.keySet().iterator();
		while (tableIter.hasNext())
		{
			String table =(String)tableIter.next();
			table = table.toLowerCase();

			if ((tables.contains(table)) && (!tables.contains(table+ "_label")) )
				strBuf.append( table+ "_label , ");
		}
		return strBuf.toString();
	}
	/**
	 *  Description of the Method
	 *
	 *@param  groups                     Description of the Parameter
	 *@return                            Description of the Return Value
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	public static String getGroupList(ArrayList groups)
		 throws WorkflowStepException
	{
		try
		{

			if (groups.size() == 0)
			{
				return (" (Public) ");
			}

			StringBuffer groupList = new StringBuffer(" ( ");
			for (int i = 0; i < groups.size(); i++)
			{
				//MraldOutFile.logToFile( Config.getProperty("LOGFILE") , "Lattice QUery : get Group List: " +  ((LatticeNode) groups.get(i)).getName() );

				groupList.append("'" + MiscUtils.checkApostrophe(  ((LatticeNode) groups.get(i)).getName()) + "'");
				if (i != (groups.size() - 1))
				{
					groupList.append(", ");
				}
			}

			groupList.append(" ) ");

			return new String(groupList);
		} catch (NullPointerException e)
		{
			WorkflowStepException we = new WorkflowStepException(e.getMessage());
			we.fillInStackTrace();
			throw we;
		}

	}


	/**
	 *  Description of the Method
	 *
	 *@param  msg                        Description of the Parameter
	 *@return                            Description of the Return Value
	 *@exception  WorkflowStepException  Description of the Exception
	 */
	public static User getUser(MsgObject msg)
		 throws WorkflowStepException
	{
		try
		{

			HttpServletRequest req = msg.getReq();
			HttpSession ses = req.getSession();

			User user = null;
			HttpServletResponse resp = null;
//			String keyGroup;

			if (ses == null)
			{
				resp = msg.getRes();

				resp.sendRedirect(Config.getProperty("BaseUrl") + "/LatticeLogin.jsp?pageurl=" + req.getRequestURL());
				return null;
			} else
			{
				user = (User) ses.getAttribute(Config.getProperty("cookietag"));
				if (user == null)
				{
					resp.sendRedirect(Config.getProperty("BaseUrl") + "/LatticeLogin.jsp?pageurl=" + req.getRequestURL());
				}
			}


			return user;
		} catch (NullPointerException e)
		{
			WorkflowStepException we = new WorkflowStepException(e.getMessage());
			we.fillInStackTrace();
			throw we;
		} catch (java.io.IOException e)
		{
			WorkflowStepException we = new WorkflowStepException(e.getMessage());
			we.fillInStackTrace();
			throw we;
		}
	}

}

